/**
 * Error handling utilities for Vision Analyzer
 * Provides standardized error types and error handling patterns
 */

const util = require('util');

/**
 * Base Vision Analyzer Error class
 */
class VisionAnalyzerError extends Error {
  constructor(message, code, details = {}) {
    super(message);
    this.name = this.constructor.name;
    this.code = code;
    this.details = details;
    this.timestamp = new Date().toISOString();
    Error.captureStackTrace(this, this.constructor);
  }

  /**
   * Convert error to JSON object
   * @returns {Object} Error object representation
   */
  toJSON() {
    return {
      name: this.name,
      message: this.message,
      code: this.code,
      details: this.details,
      timestamp: this.timestamp,
      stack: this.stack
    };
  }

  /**
   * Convert error to string representation
   * @returns {string} String representation of error
   */
  toString() {
    return `${this.name} [${this.code}]: ${this.message}`;
  }
}

/**
 * Configuration-related errors
 */
class ConfigurationError extends VisionAnalyzerError {
  constructor(message, details = {}) {
    super(message, 'CONFIG_ERROR', details);
  }
}

/**
 * Validation-related errors
 */
class ValidationError extends VisionAnalyzerError {
  constructor(message, validationErrors = []) {
    super(message, 'VALIDATION_ERROR', { validationErrors });
  }
}

/**
 * Provider-related errors
 */
class ProviderError extends VisionAnalyzerError {
  constructor(message, provider, details = {}) {
    super(message, 'PROVIDER_ERROR', { provider, ...details });
  }
}

/**
 * API-related errors
 */
class APIError extends VisionAnalyzerError {
  constructor(message, statusCode, response = {}) {
    super(message, 'API_ERROR', { statusCode, response });
  }
}

/**
 * File system-related errors
 */
class FileSystemError extends VisionAnalyzerError {
  constructor(message, filePath, operation) {
    super(message, 'FILESYSTEM_ERROR', { filePath, operation });
  }
}

/**
 * Network-related errors
 */
class NetworkError extends VisionAnalyzerError {
  constructor(message, url, details = {}) {
    super(message, 'NETWORK_ERROR', { url, ...details });
  }
}

/**
 * Timeout-related errors
 */
class TimeoutError extends VisionAnalyzerError {
  constructor(message, timeout, operation) {
    super(message, 'TIMEOUT_ERROR', { timeout, operation });
  }
}

/**
 * MCP protocol-related errors
 */
class MCPError extends VisionAnalyzerError {
  constructor(message, requestId, details = {}) {
    super(message, 'MCP_ERROR', { requestId, ...details });
  }
}

/**
 * Error factory for creating appropriate error instances
 */
class ErrorFactory {
  /**
   * Create error from error object or string
   * @param {Error|string} error - Error to convert
   * @param {string} defaultCode - Default error code if none specified
   * @returns {VisionAnalyzerError} Appropriate error instance
   */
  static create(error, defaultCode = 'UNKNOWN_ERROR') {
    if (error instanceof VisionAnalyzerError) {
      return error;
    }

    if (error instanceof Error) {
      // Convert standard errors to VisionAnalyzerError
      const message = error.message || 'Unknown error occurred';
      const code = error.code || defaultCode;

      // Try to match error type based on error name or message
      if (error.name === 'ValidationError' || message.includes('validation')) {
        return new ValidationError(message);
      }
      if (error.name === 'FileSystemError' || message.includes('ENOENT') || message.includes('EACCES')) {
        return new FileSystemError(message, error.path, 'unknown');
      }
      if (error.name === 'NetworkError' || message.includes('ECONNREFUSED') || message.includes('ETIMEDOUT')) {
        return new NetworkError(message, error.address || 'unknown');
      }

      // Default to generic VisionAnalyzerError
      return new VisionAnalyzerError(message, code, { originalError: error });
    }

    if (typeof error === 'string') {
      return new VisionAnalyzerError(error, defaultCode);
    }

    return new VisionAnalyzerError('Unknown error occurred', defaultCode, { originalError: error });
  }

  /**
   * Create configuration error
   * @param {string} message - Error message
   * @param {Object} details - Error details
   * @returns {ConfigurationError} Configuration error instance
   */
  static configuration(message, details = {}) {
    return new ConfigurationError(message, details);
  }

  /**
   * Create validation error
   * @param {string} message - Error message
   * @param {Array} validationErrors - Validation error list
   * @returns {ValidationError} Validation error instance
   */
  static validation(message, validationErrors = []) {
    return new ValidationError(message, validationErrors);
  }

  /**
   * Create provider error
   * @param {string} message - Error message
   * @param {string} provider - Provider name
   * @param {Object} details - Error details
   * @returns {ProviderError} Provider error instance
   */
  static provider(message, provider, details = {}) {
    return new ProviderError(message, provider, details);
  }

  /**
   * Create API error
   * @param {string} message - Error message
   * @param {number} statusCode - HTTP status code
   * @param {Object} response - Response data
   * @returns {APIError} API error instance
   */
  static api(message, statusCode, response = {}) {
    return new APIError(message, statusCode, response);
  }

  /**
   * Create file system error
   * @param {string} message - Error message
   * @param {string} filePath - File path
   * @param {string} operation - File operation
   * @returns {FileSystemError} File system error instance
   */
  static filesystem(message, filePath, operation = 'unknown') {
    return new FileSystemError(message, filePath, operation);
  }

  /**
   * Create network error
   * @param {string} message - Error message
   * @param {string} url - URL
   * @param {Object} details - Error details
   * @returns {NetworkError} Network error instance
   */
  static network(message, url, details = {}) {
    return new NetworkError(message, url, details);
  }

  /**
   * Create timeout error
   * @param {string} message - Error message
   * @param {number} timeout - Timeout in milliseconds
   * @param {string} operation - Operation that timed out
   * @returns {TimeoutError} Timeout error instance
   */
  static timeout(message, timeout, operation = 'unknown') {
    return new TimeoutError(message, timeout, operation);
  }

  /**
   * Create MCP error
   * @param {string} message - Error message
   * @param {string} requestId - MCP request ID
   * @param {Object} details - Error details
   * @returns {MCPError} MCP error instance
   */
  static mcp(message, requestId, details = {}) {
    return new MCPError(message, requestId, details);
  }
}

/**
 * Error handler utility class
 */
class ErrorHandler {
  /**
   * Handle error with appropriate logging and response
   * @param {Error} error - Error to handle
   * @param {Object} context - Error context
   * @param {Function} logger - Logger function
   * @returns {VisionAnalyzerError} Standardized error
   */
  static handle(error, context = {}, logger = console.error) {
    const standardError = ErrorFactory.create(error);

    // Log the error with context
    logger('Error occurred:', {
      error: standardError.toJSON(),
      context: context
    });

    return standardError;
  }

  /**
   * Handle async error in promise chains
   * @param {Error} error - Error to handle
   * @param {Object} context - Error context
   * @returns {Promise} Promise that rejects with standardized error
   */
  static async handleAsync(error, context = {}) {
    return Promise.reject(ErrorFactory.create(error));
  }

  /**
   * Wrap async function with error handling
   * @param {Function} fn - Async function to wrap
   * @param {Object} context - Error context
   * @returns {Function} Wrapped function
   */
  static wrap(fn, context = {}) {
    return async (...args) => {
      try {
        return await fn(...args);
      } catch (error) {
        throw ErrorHandler.handle(error, context);
      }
    };
  }

  /**
   * Check if error is recoverable
   * @param {Error} error - Error to check
   * @returns {boolean} Whether error is recoverable
   */
  static isRecoverable(error) {
    if (error instanceof VisionAnalyzerError) {
      // Network errors and timeouts are often recoverable
      return ['NETWORK_ERROR', 'TIMEOUT_ERROR', 'API_ERROR'].includes(error.code);
    }

    // Default to non-recoverable for unknown errors
    return false;
  }

  /**
   * Get suggested retry delay based on error type
   * @param {Error} error - Error to check
   * @param {number} attempt - Current attempt number
   * @returns {number} Retry delay in milliseconds
   */
  static getRetryDelay(error, attempt = 1) {
    if (!ErrorHandler.isRecoverable(error)) {
      return 0; // Don't retry non-recoverable errors
    }

    // Exponential backoff with jitter
    const baseDelay = 1000; // 1 second
    const maxDelay = 30000; // 30 seconds
    const exponentialDelay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
    const jitter = Math.random() * 0.1 * exponentialDelay; // 10% jitter

    return Math.round(exponentialDelay + jitter);
  }
}

module.exports = {
  VisionAnalyzerError,
  ConfigurationError,
  ValidationError,
  ProviderError,
  APIError,
  FileSystemError,
  NetworkError,
  TimeoutError,
  MCPError,
  ErrorFactory,
  ErrorHandler
};